#ifndef GATEWAY_SERVER_H
#define GATEWAY_SERVER_H

#include <memory>
#include <thread>

#include "channel.h"
#include "connection.h"
#include "data_redis.h"
#include "etcd.h"
#include "file.pb.h"
#include "friend.pb.h"
#include "http_path.h"
#include "httplib.h"
#include "message.pb.h"
#include "speech.pb.h"
#include "transmite.pb.h"
#include "user.pb.h"

namespace chat {
class GatewayServer {
   public:
    GatewayServer(uint16_t ws_port, uint16_t http_port,
                  const std::shared_ptr<sw::redis::Redis>& redis_client,
                  const ServiceManagerPtr& service_manager,
                  const std::string& file_service_name,
                  const std::string& user_service_name,
                  const std::string& speech_service_name,
                  const std::string& transmit_service_name,
                  const std::string& message_service_name,
                  const std::string& friend_service_name,
                  const DiscoveryPtr& discovery_client)
        : redis_session_(std::make_shared<Session>(redis_client)),
          redis_status_(std::make_shared<Status>(redis_client)),
          service_manager_(service_manager),
          file_service_name_(file_service_name),
          user_service_name_(user_service_name),
          speech_service_name_(speech_service_name),
          transmit_service_name_(transmit_service_name),
          message_service_name_(message_service_name),
          friend_service_name_(friend_service_name),
          discovery_client_(discovery_client) {
        initWebSocketServer(ws_port);
        initHttpServer();
        http_thread_ = std::make_shared<std::thread>(
            [this, http_port]() { http_server_->listen("0.0.0.0", http_port); });
        http_thread_->detach();
    }

    void start() { ws_server_->run(); }

   private:
    void handleGetPhoneVerifyCode(const httplib::Request& request,
                                  httplib::Response& response);
    void handleUsernameRegister(const httplib::Request& request,
                                httplib::Response& response);
    void handleUsernameLogin(const httplib::Request& request,
                             httplib::Response& response);
    void handlePhoneRegister(const httplib::Request& request,
                             httplib::Response& response);
    void handlePhoneLogin(const httplib::Request& request,
                          httplib::Response& response);
    void handleGetUserInfo(const httplib::Request& request,
                           httplib::Response& response);
    void handleSetAvatar(const httplib::Request& request,
                         httplib::Response& response);
    void handleSetNickname(const httplib::Request& request,
                           httplib::Response& response);
    void handleSetDescription(const httplib::Request& request,
                              httplib::Response& response);
    void handleSetPhone(const httplib::Request& request,
                        httplib::Response& response);
                        
    void handleGetFriendList(const httplib::Request& request,
                             httplib::Response& response);
    void handleAddFriend(const httplib::Request& request,
                              httplib::Response& response);
    void handleAddFriendProcess(const httplib::Request& request,
                                httplib::Response& response);
    void handleRemoveFriend(const httplib::Request& request,
                            httplib::Response& response);
    void handleSearchFriend(const httplib::Request& request,
                            httplib::Response& response);
    void handleGetChatSessionList(const httplib::Request& request,
                                  httplib::Response& response);
    void handleCreateChatSession(const httplib::Request& request,
                                 httplib::Response& response);
    void handleGetChatSessionMember(const httplib::Request& request,
                                    httplib::Response& response);
    void handleGetPendingFriendEvents(const httplib::Request& request,
                                      httplib::Response& response);

    void handleGetMessageStorageHistory(const httplib::Request& request,
                                        httplib::Response& response);
    void handleGetMessageStorageRecent(const httplib::Request& request,
                                       httplib::Response& response);
    void handleSearchMessageStorageHistory(const httplib::Request& request,
                                           httplib::Response& response);

    void handleGetSingleFile(const httplib::Request& request,
                             httplib::Response& response);
    void handleGetMultiFile(const httplib::Request& request,
                            httplib::Response& response);
    void handlePutSingleFile(const httplib::Request& request,
                             httplib::Response& response);
    void handlePutMultiFile(const httplib::Request& request,
                            httplib::Response& response);

    void handleSpeechRecognition(const httplib::Request& request,
                                 httplib::Response& response);

    void handleNewMessage(const httplib::Request& request,
                          httplib::Response& response);

    void initHttpServer();
    void initWebSocketServer(uint16_t ws_port);

    void openHandler(websocketpp::connection_hdl hdl);
    void closeHandler(websocketpp::connection_hdl hdl);

    bool getUserInfo(const std::string& user_id, UserInfo* user_info);

    /**
     * @note: 客户端与网关建立长连接后，客户端会向网关发送身份验证信息，
     * 网关会验证客户端的身份，如果验证通过，会将客户端的身份信息存储在redis中，
     * 并将客户端的长连接与身份信息关联起来。
     * 后续客户端与网关的通信，都需要先验证身份：
     * 验证通过后，才能进行通信。
     * 验证失败，会断开客户端的长连接。
     */
    void messageHandler(server_t* server, websocketpp::connection_hdl hdl,
                        server_t::message_ptr msg);

   private:
    Connection connections_;  // 管理客户端和网关所有的长连接

    SessionPtr redis_session_ = nullptr;
    StatusPtr redis_status_ = nullptr;

    ServiceManagerPtr service_manager_ = nullptr;
    std::string file_service_name_;
    std::string user_service_name_;
    std::string speech_service_name_;
    std::string transmit_service_name_;
    std::string message_service_name_;
    std::string friend_service_name_;

    DiscoveryPtr discovery_client_ = nullptr;

    std::shared_ptr<server_t> ws_server_ = nullptr;
    std::shared_ptr<httplib::Server> http_server_ = nullptr;

    std::shared_ptr<std::thread> http_thread_;
};
using GatewayServerPtr = std::shared_ptr<GatewayServer>;

class GateWayServerBuilder {
   public:
    /**
     * @brief 创建rpc服务管理对象
     *
     * @param service_name[in]: 要关心的服务名称
     */
    void makeServiceManager(const std::string& file_service_name,
                            const std::string& user_service_name,
                            const std::string& speech_service_name,
                            const std::string& transmit_service_name,
                            const std::string& message_service_name,
                            const std::string& friend_service_name);

    /**
     * @brief 创建服务发现客户端
     * @param etcd_host[in]: etcd服务器 ip:port
     * @param base_service[in]: 服务发现时的根目录
     */
    void makeDiscoveryClient(const std::string& etcd_host,
                             const std::string& base_service);

    /**
     * @brief 创建redis客户端
     *
     * @param ip[in]: redis服务器ip
     * @param port[in]: redis服务器端口
     * @param db[in]: 库的编号，默认为0
     * @param keep_alive[in]: 是否设置长连接保活
     */
    void makeRedisClient(const std::string& ip, const uint16_t port,
                         const int db, const bool keep_alive);

    /**
     * @brief 创建 http websocket服务端
     *
     * @param ws_port[in]: websocket端口
     * @param http_port[in]: http端口
     */
    void makeServer(uint16_t ws_port, uint16_t http_port);

    /**
     * @brief 构造一个 GatewayServer对象 并返回
     */
    GatewayServerPtr build();

   private:
    uint16_t ws_port_;
    uint16_t http_port_;

    RegistryPtr register_client_ = nullptr;
    DiscoveryPtr discovery_client_ = nullptr;

    ServiceManagerPtr service_manager_ = nullptr;
    std::string file_service_name_;
    std::string user_service_name_;
    std::string speech_service_name_;
    std::string transmit_service_name_;
    std::string message_service_name_;
    std::string friend_service_name_;

    std::shared_ptr<brpc::Server> brpc_server_ = nullptr;

    std::shared_ptr<sw::redis::Redis> redis_client_ = nullptr;
};
}  // namespace chat

#endif  // GATEWAY_SERVER_H